{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "ec420a7d-9ff0-4e83-8b25-5e6ec1422900",
   "metadata": {},
   "source": [
    "# BERTScore From Scratch"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "f962b7b7-a42b-4422-a7d3-27b70bf02d1f",
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch\n",
    "import numpy as np\n",
    "from transformers import BertTokenizer, BertModel\n",
    "\n",
    "def cosine_similarity(a, b):\n",
    "    return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))\n",
    "\n",
    "def bert_score(reference, candidate, return_similarity_matrix=False):\n",
    "    \n",
    "    # Load the BERT tokenizer and model\n",
    "    tokenizer = BertTokenizer.from_pretrained(\"bert-base-uncased\")\n",
    "    model = BertModel.from_pretrained(\"bert-base-uncased\")\n",
    "    \n",
    "    # Tokenize the input text\n",
    "    ref_tokens = tokenizer(reference, return_tensors=\"pt\", add_special_tokens=False)\n",
    "    can_tokens = tokenizer(candidate, return_tensors=\"pt\", add_special_tokens=False)\n",
    "\n",
    "    # Get the BERT embeddings\n",
    "    model.eval()\n",
    "    with torch.no_grad():\n",
    "        ref_outputs = model(**ref_tokens)\n",
    "        ref_embeddings = ref_outputs.last_hidden_state[0]\n",
    "\n",
    "        can_outputs = model(**can_tokens)\n",
    "        can_embeddings = can_outputs.last_hidden_state[0]\n",
    "        \n",
    "    # Compute cosine similarities\n",
    "    cosine_similarities = np.zeros((can_embeddings.shape[0], ref_embeddings.shape[0]))\n",
    "    for i, c in enumerate(can_embeddings):\n",
    "        for j, r in enumerate(ref_embeddings):\n",
    "            cosine_similarities[i, j] = cosine_similarity(c, r)\n",
    "    \n",
    "\n",
    "    # Align cosine similarities\n",
    "    max_similarities = cosine_similarities.max(axis=1)\n",
    "\n",
    "    # Average similarity scores\n",
    "    bertscore = max_similarities.mean()\n",
    "\n",
    "    if return_similarity_matrix:\n",
    "        return bertscore, cosine_similarities\n",
    "    else:\n",
    "        return bertscore"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "46883444-11af-46a3-bb2a-22329c3558ae",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Some weights of the model checkpoint at bert-base-uncased were not used when initializing BertModel: ['cls.seq_relationship.weight', 'cls.predictions.transform.LayerNorm.weight', 'cls.predictions.decoder.weight', 'cls.predictions.bias', 'cls.predictions.transform.dense.weight', 'cls.seq_relationship.bias', 'cls.predictions.transform.LayerNorm.bias', 'cls.predictions.transform.dense.bias']\n",
      "- This IS expected if you are initializing BertModel from the checkpoint of a model trained on another task or with another architecture (e.g. initializing a BertForSequenceClassification model from a BertForPreTraining model).\n",
      "- This IS NOT expected if you are initializing BertModel from the checkpoint of a model that you expect to be exactly identical (initializing a BertForSequenceClassification model from a BertForSequenceClassification model).\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "0.8767206933763292"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Define the input text\n",
    "reference = \"The quick brown fox jumps over the lazy dog.\"\n",
    "candidate = \"A quick brown fox leaps over a dog.\"\n",
    "\n",
    "bert_score(reference, candidate)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "e3494bb3-9742-4cf9-b339-80436b4e4e3b",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Some weights of the model checkpoint at bert-base-uncased were not used when initializing BertModel: ['cls.seq_relationship.weight', 'cls.predictions.transform.LayerNorm.weight', 'cls.predictions.decoder.weight', 'cls.predictions.bias', 'cls.predictions.transform.dense.weight', 'cls.seq_relationship.bias', 'cls.predictions.transform.LayerNorm.bias', 'cls.predictions.transform.dense.bias']\n",
      "- This IS expected if you are initializing BertModel from the checkpoint of a model trained on another task or with another architecture (e.g. initializing a BertForSequenceClassification model from a BertForPreTraining model).\n",
      "- This IS NOT expected if you are initializing BertModel from the checkpoint of a model that you expect to be exactly identical (initializing a BertForSequenceClassification model from a BertForSequenceClassification model).\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "0.6320915818214417"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "bert_score(\"fast\", \"quick\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "f4ac45db-1227-46b2-bb02-9d4eebcba5d2",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Some weights of the model checkpoint at bert-base-uncased were not used when initializing BertModel: ['cls.seq_relationship.weight', 'cls.predictions.transform.LayerNorm.weight', 'cls.predictions.decoder.weight', 'cls.predictions.bias', 'cls.predictions.transform.dense.weight', 'cls.seq_relationship.bias', 'cls.predictions.transform.LayerNorm.bias', 'cls.predictions.transform.dense.bias']\n",
      "- This IS expected if you are initializing BertModel from the checkpoint of a model trained on another task or with another architecture (e.g. initializing a BertForSequenceClassification model from a BertForPreTraining model).\n",
      "- This IS NOT expected if you are initializing BertModel from the checkpoint of a model that you expect to be exactly identical (initializing a BertForSequenceClassification model from a BertForSequenceClassification model).\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "1.0"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "bert_score(\"fast\", \"fast\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "ec6c369c-0350-4a7d-8442-051d2a7be83c",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Some weights of the model checkpoint at bert-base-uncased were not used when initializing BertModel: ['cls.seq_relationship.weight', 'cls.predictions.transform.LayerNorm.weight', 'cls.predictions.decoder.weight', 'cls.predictions.bias', 'cls.predictions.transform.dense.weight', 'cls.seq_relationship.bias', 'cls.predictions.transform.LayerNorm.bias', 'cls.predictions.transform.dense.bias']\n",
      "- This IS expected if you are initializing BertModel from the checkpoint of a model trained on another task or with another architecture (e.g. initializing a BertForSequenceClassification model from a BertForPreTraining model).\n",
      "- This IS NOT expected if you are initializing BertModel from the checkpoint of a model that you expect to be exactly identical (initializing a BertForSequenceClassification model from a BertForSequenceClassification model).\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.685902863740921\n",
      "[[0.63398284 0.52621418 0.51968789]\n",
      " [0.59454805 0.71506584 0.68891364]\n",
      " [0.55240393 0.68978393 0.69811189]\n",
      " [0.55321109 0.69164938 0.69645089]]\n"
     ]
    }
   ],
   "source": [
    "reference = \"The cat jumped\"\n",
    "candidate = \"A cat leaped high\"\n",
    "\n",
    "# Compute BERTScore\n",
    "bertscore, sim_matrix = bert_score(\n",
    "    reference, candidate, return_similarity_matrix=True)\n",
    "\n",
    "print(bertscore)\n",
    "print(sim_matrix)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "cf7f4778-9aa2-4ef5-83de-cbeb31e5b15a",
   "metadata": {},
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "\n",
    "\n",
    "def heatmap(\n",
    "    matrix,\n",
    "    hide_spines=False,\n",
    "    hide_ticks=False,\n",
    "    figsize=None,\n",
    "    cmap=None,\n",
    "    colorbar=True,\n",
    "    row_names=None,\n",
    "    column_names=None,\n",
    "    column_name_rotation=45,\n",
    "    cell_values=True,\n",
    "    cell_fmt=\".2f\",\n",
    "    cell_font_size=None,\n",
    "):\n",
    "\n",
    "    if row_names is not None and len(row_names) != matrix.shape[0]:\n",
    "        raise AssertionError(\n",
    "            f\"len(row_names) (got {len(row_names)})\"\n",
    "            \" should be equal to number of\"\n",
    "            \" rows in the input \"\n",
    "            f\" array (expect {matrix.shape[0]}).\"\n",
    "        )\n",
    "\n",
    "    if column_names is not None and len(column_names) != matrix.shape[1]:\n",
    "        raise AssertionError(\n",
    "            f\"len(column_names)\"\n",
    "            \" (got {len(column_names)})\"\n",
    "            \" should be equal to number of\"\n",
    "            \" columns in the\"\n",
    "            f\" input array (expect {matrix.shape[1]}).\"\n",
    "        )\n",
    "\n",
    "    fig, ax = plt.subplots(figsize=figsize)\n",
    "    ax.grid(False)\n",
    "\n",
    "    if cmap is None:\n",
    "        cmap = plt.cm.viridis\n",
    "\n",
    "    if figsize is None:\n",
    "        figsize = (len(matrix) * 1.5, len(matrix) * 1.5)\n",
    "\n",
    "    matshow = ax.matshow(matrix, cmap=cmap)\n",
    "\n",
    "    if colorbar:\n",
    "        fig.colorbar(matshow)\n",
    "\n",
    "    normed_matrix = matrix.astype(\"float\") / matrix.max()\n",
    "\n",
    "    if cell_values:\n",
    "        for i in range(matrix.shape[0]):\n",
    "            for j in range(matrix.shape[1]):\n",
    "                cell_text = format(matrix[i, j], cell_fmt)\n",
    "\n",
    "                ax.text(\n",
    "                    x=j,\n",
    "                    y=i,\n",
    "                    size=cell_font_size,\n",
    "                    s=cell_text,\n",
    "                    va=\"center\",\n",
    "                    ha=\"center\",\n",
    "                    color=\"black\"\n",
    "                    if normed_matrix[i, j] > np.max(normed_matrix) / 2\n",
    "                    else \"black\",\n",
    "                )\n",
    "\n",
    "    if row_names is not None:\n",
    "        tick_marks = np.arange(len(row_names))\n",
    "        plt.yticks(tick_marks, row_names)\n",
    "\n",
    "    if column_names is not None:\n",
    "        tick_marks = np.arange(len(column_names))\n",
    "\n",
    "        if column_name_rotation:\n",
    "            plt.xticks(\n",
    "                tick_marks,\n",
    "                column_names,\n",
    "                rotation=column_name_rotation,\n",
    "                ha=\"right\",\n",
    "                rotation_mode=\"anchor\",\n",
    "            )\n",
    "        else:\n",
    "            plt.xticks(tick_marks, column_names)\n",
    "\n",
    "    if hide_spines:\n",
    "        ax.spines[\"right\"].set_visible(False)\n",
    "        ax.spines[\"top\"].set_visible(False)\n",
    "        ax.spines[\"left\"].set_visible(False)\n",
    "        ax.spines[\"bottom\"].set_visible(False)\n",
    "    ax.yaxis.set_ticks_position(\"left\")\n",
    "    ax.xaxis.set_ticks_position(\"bottom\")\n",
    "    if hide_ticks:\n",
    "        ax.axes.get_yaxis().set_ticks([])\n",
    "        ax.axes.get_xaxis().set_ticks([])\n",
    "\n",
    "    return fig, ax"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "bf64bd2a-77eb-49ed-be75-6cb4e5e32dd7",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 400x400 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from matplotlib import cm\n",
    "from matplotlib.colors import ListedColormap\n",
    "\n",
    "viridisBig = cm.get_cmap('Blues', 512)\n",
    "newcmp = ListedColormap(viridisBig(np.linspace(0.0, 0.5, 256)))\n",
    "\n",
    "fig, ax = heatmap(sim_matrix, column_names=reference.split(), row_names=candidate.split(), cmap=newcmp, figsize=(4, 4))\n",
    "\n",
    "plt.tight_layout()\n",
    "\n",
    "#plt.savefig('matrix.pdf')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1f78877c-aa5f-450b-a683-925a2346a1d3",
   "metadata": {},
   "source": [
    "# Using the Official BERTScore package"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bb0a0a7b-3c61-4caf-af5d-e9c78dd1e1b2",
   "metadata": {},
   "source": [
    "See https://github.com/Tiiiger/bert_score"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "73533cb5-2c93-4be9-b4b5-266932fbc3fc",
   "metadata": {},
   "outputs": [],
   "source": [
    "# pip install bert-score"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "ac66de0d-24dc-47ba-a388-b060b8f3d4ff",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Some weights of the model checkpoint at bert-base-uncased were not used when initializing BertModel: ['cls.predictions.decoder.weight', 'cls.predictions.transform.LayerNorm.bias', 'cls.predictions.bias', 'cls.seq_relationship.weight', 'cls.predictions.transform.dense.bias', 'cls.seq_relationship.bias', 'cls.predictions.transform.LayerNorm.weight', 'cls.predictions.transform.dense.weight']\n",
      "- This IS expected if you are initializing BertModel from the checkpoint of a model trained on another task or with another architecture (e.g. initializing a BertForSequenceClassification model from a BertForPreTraining model).\n",
      "- This IS NOT expected if you are initializing BertModel from the checkpoint of a model that you expect to be exactly identical (initializing a BertForSequenceClassification model from a BertForSequenceClassification model).\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Precision: 0.9040\n",
      "Recall: 0.9040\n",
      "F1 Score: 0.9040\n"
     ]
    }
   ],
   "source": [
    "import torch\n",
    "from bert_score import score\n",
    "\n",
    "# Define the reference and candidate texts\n",
    "reference = \"The quick brown fox jumps over the lazy dog.\"\n",
    "candidate = \"A quick brown fox leaps over a lazy dog.\"\n",
    "\n",
    "# Compute BERTScore (P, R, F1)\n",
    "P, R, F1 = score([candidate], [reference], lang='en', model_type='bert-base-uncased',\n",
    "                 device=torch.device('cuda' if torch.cuda.is_available() else 'cpu'))\n",
    "\n",
    "# Print the BERTScore results\n",
    "print(f\"Precision: {P.item():.4f}\")\n",
    "print(f\"Recall: {R.item():.4f}\")\n",
    "print(f\"F1 Score: {F1.item():.4f}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7887c143-53cd-442a-b0c4-7710da407cb5",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
